package com.leyantech.ecp.agenx.common.entity.reply;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;
import static com.leyantech.ai.DialogueResponse.DialogueResponseCase.FORCESENDRESPONSE;
import static com.leyantech.ai.DialogueResponse.DialogueResponseCase.NOOPRESPONSE;
import static com.leyantech.ai.DialogueResponse.DialogueResponseCase.REPLYRESPONSE;
import static com.leyantech.ecp.agenx.common.config.ConfigManager.ITEMCARD_EXPIRE_MILLS;

import com.leyantech.ai.DialogueReply;
import com.leyantech.ai.DialogueReplyContext;
import com.leyantech.ai.DialogueReplyContext.ReplyCommand;
import com.leyantech.ai.DialogueReplyV2;
import com.leyantech.ai.DialogueResponse;
import com.leyantech.ai.DialogueResponse.DialogueResponseCase;
import com.leyantech.ai.DialogueSentence;
import com.leyantech.ai.DialogueSentence.ValueCase;
import com.leyantech.ai.Extension;
import com.leyantech.ai.NoopResponse;
import com.leyantech.ai.ServiceMode;
import com.leyantech.ecp.agenx.common.redis.RedissonOperation;
import com.leyantech.ecp.agenx.common.utils.PbPrintHelper;
import com.leyantech.leyan.usercenter.AnswerMode;
import com.leyantech.utility.Logger;

import com.google.common.collect.Lists;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zekun.dong, {@literal <zekun.dong@leyantech.com>}
 * @date 2020-06-05.
 */
public class ReplyContext {

  private static final Logger LOGGER = new Logger(ReplyContext.class);

  private String requestId;

  @Getter
  @Setter
  private DialogueReplyV2 reply;

  private List<DialogueSentence> sentences;

  private List<DialogueSentence> textContent = newArrayList();

  private List<DialogueSentence> imageContent = newArrayList();

  private List<String> originImageIds = newArrayList();

  @Setter
  private String assistantId;

  private String buyerNick;

  @Setter
  private String storeId;

  private String sessionId;

  private ReplyCommand replyCommand;

  /**
   * 是否转人工.
   */
  @Setter
  private boolean switchToHuman;

  @Getter
  private String orderId;

  /**
   * 设备号.
   */
  @Getter
  private String deviceNo;

  /**
   * 是否是表情图片.
   */
  private boolean containsExpression;
  private AnswerMode keepAnswerMode;
  private ServiceMode serviceMode;
  private String messageOwner;
  
  @Setter
  @Getter
  private String notReplyReason;
  
  @Getter
  private DialogueReplyContext dialogueReplyContext;

  @Getter
  private JsonArray notReplyJsonList = new JsonArray();
  
  /**
   * create dialogue reply context.
   *
   * @param dialogueReply dialogueReply
   * @return MessageContext
   */
  public static ReplyContext create(DialogueReply dialogueReply) {
    // 检查必要参数
    DialogueReplyV2 replyV2 = dialogueReply.getDialogueReplyV2();
    String requestId;
    requestId = replyV2.getUuid();
    checkArgument(StringUtils.isNotBlank(requestId), "requestId is null or empty. sessionId %s",
        dialogueReply.getSessionId());
    checkArgument(StringUtils.isNotBlank(replyV2.getStoreId()),
        "storeId is null or empty. sessionId %s",
        dialogueReply.getSessionId());
    checkArgument(StringUtils.isNotBlank(replyV2.getAssistantId()),
        "assistantNick is null or empty. sessionId %s",
        dialogueReply.getSessionId());
    checkArgument(StringUtils.isNotBlank(replyV2.getUserNick()),
        "buyerNick is null or empty. sessionId %s",
        dialogueReply.getSessionId());

    ReplyContext context = new ReplyContext();
    context.init(dialogueReply);
    return context;
  }

  protected void init(DialogueReply dialogueReply) {
    this.reply = dialogueReply.getDialogueReplyV2();
    this.requestId = reply.getUuid();
    this.assistantId = reply.getAssistantId();
    this.buyerNick = reply.getUserNick();
    this.sentences = extractSentenceFromDialogueReply(dialogueReply);
    this.initContent();
    this.switchToHuman = this.checkSwitchToHuman(dialogueReply);
    this.storeId = dialogueReply.getStoreId();
    this.sessionId = dialogueReply.getSessionId();
    this.replyCommand = dialogueReply.getContext().getReplyCommand();

    this.keepAnswerMode = dialogueReply.getKeepingAnswerMode();
    this.serviceMode = dialogueReply.getServiceMode();
    this.messageOwner = resolveMessageOwner(dialogueReply);
    
    this.dialogueReplyContext = dialogueReply.getContext();
    
    // 2021-05-31 10:55:06 添加 extension.orderId
    if (dialogueReply.hasExtension()) {
      Extension extension = dialogueReply.getExtension();
      this.orderId = extension.getOrderId();
      this.deviceNo = extension.getDeviceNo();
    }
  }


  /**
   * 将转人工置为true，并且将回答置为NoopResponse.
   */
  public void updateContentToSwitchToHuman() {
    this.switchToHuman = true;
    DialogueResponse dialogueResponse = DialogueResponse.newBuilder()
        .setNoopResponse(NoopResponse.getDefaultInstance())
        .build();
    if (this.reply == null) {
      this.reply = DialogueReplyV2.newBuilder().addAllDialogueResponse(Lists.newArrayList(dialogueResponse)).build();
    } else {
      this.reply = this.reply.toBuilder().clearDialogueResponse()
          .addAllDialogueResponse(Lists.newArrayList(dialogueResponse)).build();
    }
  }

  // 一般回复的response.
  private static final List<DialogueResponseCase> replyResponseCaseList = Arrays
      .asList(DialogueResponseCase.FORCESENDRESPONSE, DialogueResponseCase.REPLYRESPONSE);

  public void takeNotReplyResponse() {

    reply.getDialogueResponseList()
        .stream()
        .filter(dialogueResponse ->
            !replyResponseCaseList.contains(dialogueResponse.getDialogueResponseCase()))
        .map(PbPrintHelper::toJson)
        .map(JsonObject::new)
        .forEach(notReplyJsonList::add);

  }
  
  public void updateReplyToNoopWithOther() {
    this.switchToHuman = true;
    DialogueResponse noopResponse = DialogueResponse.newBuilder()
        .setNoopResponse(NoopResponse.getDefaultInstance())
        .build();
    if (this.reply == null) {
      this.reply = DialogueReplyV2.newBuilder().addAllDialogueResponse(Lists.newArrayList(noopResponse)).build();
      return;
    }

    List<DialogueResponse> filterNoReply = new ArrayList<>();
    List<DialogueResponse> dialogueResponseList = this.reply.getDialogueResponseList();

    for (DialogueResponse dialogueResponse : dialogueResponseList) {
      if (dialogueResponse.getDialogueResponseCase() == REPLYRESPONSE 
          || dialogueResponse.getDialogueResponseCase() == FORCESENDRESPONSE
      ) {
        continue;
      }
      // 不包含普通回复，可添加
      filterNoReply.add(dialogueResponse);
    }
    if (filterNoReply.isEmpty()) {
      filterNoReply.add(noopResponse);
    }
    this.reply = this.reply.toBuilder().clearDialogueResponse()
        .addAllDialogueResponse(Lists.newArrayList(filterNoReply)).build();
  }

  private String resolveMessageOwner(DialogueReply dialogueReply) {
    if (CollectionUtils.isNotEmpty(dialogueReply.getContext().getQuestionList())) {
      return dialogueReply.getContext().getQuestion(0).getExtension().getMessageOwner();
    }
    return StringUtils.EMPTY;
  }

  /**
   * 判断非表情图片关键词，包含任意一个 就认为是非表情图片.
   */
  public static final String[] NOT_EXPRESSION_KEYWORDS = new String[]{"如图", "图片", "下图", "图示", "效果图", "看看", "页面", "参考",
      "以下", "如下", "安装", "视频", "试衣间小贴士", "效果", "看一下"};

  /**
   * 处理其中的Sentence.
   */
  private void initContent() {
    // 提取文本回复 需要把消息分隔符拆分为多个DialogueSentence
    this.textContent = this.sentences
        .stream()
        .filter(sentence -> StringUtils.isNotBlank(sentence.getText().getContent()))
        .peek(sentence -> {
          if (StringUtils.containsAny(sentence.getText().getContent(), NOT_EXPRESSION_KEYWORDS)) {
            this.containsExpression = true;
          }
        })
        .collect(Collectors.toList());

    // 提取图片DialogueSentence
    this.imageContent = this.sentences
        .stream()
        .filter(sentence -> StringUtils.isNotBlank(sentence.getImage().getImageId()))
        .collect(Collectors.toList());
    this.originImageIds = imageContent.stream()
        .map(sentence -> sentence.getImage().getImageId())
        .collect(Collectors.toList());
  }


  private List<DialogueSentence> extractSentenceFromDialogueReply(DialogueReply dialogueReply) {
    List<DialogueSentence> dialogueSentences = newArrayList();
    for (DialogueResponse dialogueResponse : dialogueReply.getDialogueReplyV2()
        .getDialogueResponseList()) {
      if (NOOPRESPONSE == dialogueResponse.getDialogueResponseCase()) {
        break;
      }

      switch (dialogueResponse.getDialogueResponseCase()) {
        case SUGGESTRESPONSE:
          dialogueSentences.addAll(dialogueResponse.getSuggestResponse().getSentenceList());
          break;
        case REPLYRESPONSE:
          dialogueSentences.addAll(dialogueResponse.getReplyResponse().getSentenceList());
          break;
        case FORCESENDRESPONSE:
          dialogueSentences.addAll(dialogueResponse.getForceSendResponse().getSentenceList());
          break;
      }
    }

    final boolean hasItemCard = dialogueSentences.stream()
        .anyMatch(dialogueSentence -> dialogueSentence.getValueCase() == ValueCase.ITEM_CARD);
    if (hasItemCard) {
      RedissonOperation.ITEM_CARD
          .executor(String.join(".", assistantId, buyerNick))
          .run(bucket -> bucket.set("", ITEMCARD_EXPIRE_MILLS.get(), TimeUnit.MILLISECONDS), "set");
    }

    return dialogueSentences;
  }

  private boolean checkSwitchToHuman(DialogueReply dialogueReply) {
    boolean result = false;

    // 辅助模式下转人工
    if (dialogueReply.getSwitchToHuman()) {
      LOGGER.debug("dialogue keeping answer mode or switch to human requestId {} sessionId {}", requestId,
          dialogueReply.getSessionId());
      result = true;
    }

    // 如果没有任何回复目前也转人工
    if (dialogueReply.getDialogueReplyV2().getDialogueResponseList().isEmpty()) {
      LOGGER.warn("dialogue no reply content. request id {}", requestId);
      result = true;
    }
    return result;
  }

  /**
   * get requestId.
   *
   * @return requestId
   */
  public String getRequestId() {
    return reply.getUuid();
  }
  /**
   * get is help mode.
   *
   * @return is help mode
   */
  public boolean isContainsExpression() {
    return containsExpression;
  }

  /**
   * get is switch to human.
   *
   * @return is switch to human
   */
  public boolean isSwitchToHuman() {
    return switchToHuman;
  }

  /**
   * get is switch to human.
   *
   * @return is switch to human
   */
  public List<DialogueSentence> getTextContent() {
    return textContent;
  }

  /**
   * get is switch to human.
   *
   * @return is switch to human
   */
  public List<DialogueSentence> getImageContent() {
    return imageContent;
  }

  /**
   * get is switch to human.
   *
   * @return is switch to human
   */
  public List<String> getOriginImageIds() {
    return originImageIds;
  }

  /**
   * 构造淘宝用户买家.
   *
   * @return User
   */
  public String getBuyerNick() {
    return buyerNick;
  }

  /**
   * 构造淘宝用户客服.
   *
   * @return User
   */
  public String getAssistantId() {
    return assistantId;
  }

  /**
   * 构造淘宝用户客服.
   *
   * @return User
   */
  public String getStoreId() {
    return storeId;
  }

  /**
   * 构造淘宝用户客服.
   *
   * @return User
   */
  public String getSessionId() {
    return sessionId;
  }

  /**
   * 构造淘宝用户客服.
   *
   * @return User
   */
  public ReplyCommand getReplyCommand() {
    return replyCommand;
  }
  
  

  public ServiceMode getServiceMode() {
    return serviceMode;
  }

  public void setServiceMode(ServiceMode serviceMode) {
    this.serviceMode = serviceMode;
  }

  @Override
  public String toString() {
    return "ReplyContext{" + "requestId='" + requestId + '\''
        + ", reply=" + PbPrintHelper.lazyToJson(reply)
        + ", sentences=" + sentences
        + ", textContent=" + textContent
        + ", imageContent=" + imageContent
        + ", originImageIds=" + originImageIds
        + ", assistantId='" + assistantId + '\''
        + ", buyerNick='" + buyerNick + '\''
        + ", storeId='" + storeId + '\''
        + ", sessionId='" + sessionId + '\''
        + ", replyCommand=" + replyCommand
        + ", switchToHuman=" + switchToHuman
        + ", containsExpression=" + containsExpression
        + ", keepAnswerMode=" + keepAnswerMode
        + ", serviceMode=" + serviceMode
        + ", messageOwner='" + messageOwner + '\''
        + '}';
  }

  public AnswerMode getKeepAnswerMode() {
    return keepAnswerMode;
  }

  public void setKeepAnswerMode(AnswerMode keepAnswerMode) {
    this.keepAnswerMode = keepAnswerMode;
  }

  /**
   * 是否不能回复.
   *
   * @return 是否不能回复.
   */
  public boolean isCanNotReply() {
    return isSwitchToHuman();
  }

  public String getMessageOwner() {
    return messageOwner;
  }

}
